# ZPsycopgDA/pool.py - ZPsycopgDA Zope product: connection pooling
#
# Copyright (C) 2004 Federico Di Gregorio <fog@initd.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version.
#
# Or, at your option this program (ZPsycopgDA) can be distributed under the
# Zope Public License (ZPL) Version 1.0, as published on the Zope web site,
# http://www.zope.org/Resources/ZPL.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY
# or FITNESS FOR A PARTICULAR PURPOSE.
#
# See the LICENSE file for details.
#
# This file was changed to work with DCOracle2
# This implementation uses SQLAlchemy pool and 
# this is not a pool of database connections but
# a pool of ResourceManagers (that have dco2 
# connection inside)
#

import threading
import sqlalchemy.pool as pool_base
import logging
logger = logging.getLogger('DCOracle2-pool.py')

_connections_pool = {}
_connections_lock = threading.Lock()
_connection_lock = threading.Lock()

class RMFactory:
    """ Factory class that instantiates ResourceManager
    """
    def __init__(self, dsn, factory):
        self.dsn = dsn
        self.factory = factory
        
    def __call__(self):
        DB = self.factory()
        return DB(self.dsn)

def getpool(dsn, create=True, factory=None):
    _connections_lock.acquire()
    try:
        if not _connections_pool.has_key(dsn) and create:
            logger.debug('getpool: crating the pool of RMs')
            try:
                _connections_pool[dsn] = \
                    pool_base.QueuePool(RMFactory(dsn, factory), max_overflow=20, pool_size=4, use_threadlocal=True)
            except Exception, err:
                logger.error('getpool: %s' % err)
                raise
    finally:
        _connections_lock.release()
    return _connections_pool[dsn]

def flushpool(dsn):
    _connections_lock.acquire()
    try:
        if dsn in _connections_pool.keys():
            _connections_pool[dsn].dispose()
            del _connections_pool[dsn]
    finally:
        _connections_lock.release()

def getconn(dsn, create=True, factory=None):
    _connection_lock.acquire()
    try:
        conn = getpool(dsn, create=create, factory=factory).connect()
    finally:
        _connection_lock.release()
    return conn

def putconn(dsn, conn, close=False):
    _connection_lock.acquire()
    try:
        if not close:
            # this returns rm to the pool
            conn.close()  #getpool(dsn).putconn(conn, close=close)    
        else:
            # this removes rm from the pool
            conn.invalidate()
    finally:
        _connection_lock.release()

# TO DO
# does getconn and putconn needs thread lock?